package nsalt.func

import chisel3._
import chisel3.util._
import chisel3.util.experimental.BoringUtils

import nsalt._
import nsalt.util._

// Simple ALU for handling AMO instructions
// Excerpted from 
// https://github.com/OSCPU/NutShell/blob/fd86beadfc47f52973270ce6109edebd2a30363b/src/main/scala/nutcore/backend/fu/LSU.scala#L170

class AtomicArithLogic extends Module with Config {

  val io = IO(new Bundle{
    val src1 = Input(UInt(XLEN.W))
    val src2 = Input(UInt(XLEN.W))
    val oper = Input(UInt(7.W))
    val isWordOp = Input(Bool())
    val result = Output(UInt(XLEN.W))
  })

  // src1: load result
  // src2: reg  result
  val src1 = io.src1
  val src2 = io.src2
  val oper = io.oper

  val isSub = !LoadStoreOperType.isAdd(oper) 
  val addRes = (src1 +& (src2 ^ Fill(XLEN, isSub))) + isSub
  val xorRes = src1 ^ src2
  val sltu = !addRes(XLEN)
  val slt = xorRes(XLEN-1) ^ sltu

  val res = LookupTreeDefault(oper(5, 0), addRes, List(
    LoadStoreOperType.amoswap -> src2,
    // LoadStoreOperType.amoadd  -> adderRes,
    LoadStoreOperType.amoxor  -> xorRes,
    LoadStoreOperType.amoand  -> (src1 & src2),
    LoadStoreOperType.amoor   -> (src1 | src2),
    LoadStoreOperType.amomin  -> Mux(slt(0), src1, src2),
    LoadStoreOperType.amomax  -> Mux(slt(0), src2, src1),
    LoadStoreOperType.amominu -> Mux(sltu(0), src1, src2),
    LoadStoreOperType.amomaxu -> Mux(sltu(0), src2, src1)
  ))

  io.result :=  Mux(io.isWordOp, SignExt(res(31,0), 64), res(XLEN-1,0))
}
